/*================ server.c ======================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "server.h"
#include "comunication.h"

int strcasecmp(const char *s1, const char *s2);
char *strdup(const char *s1);

int port = 8080;

typedef void (*ActionType)();

/*	Estados	*/
#define WP 0				/*	Waiting packet			*/
#define WRR 1				/*	Waiting packet response	*/
/*	Eventos	*/
#define PACKET 0			/*	Packet Received			*/
#define RR 1				/*	Response received		*/
#define TIMER 2				/*	Timer Event				*/
/*	BASE DE DADOS	*/
char *put_list[128];
int put_count = 0;

char *ID;
int STATE; /* 0 = RED; 1 = YELLOW; 2 = GREEN */
int LABEL;
char* SON;
char *in_pdu;
char *out_pdu;
char *message;
int servers_access[COUNTRIES_LIST_SIZE];

int machine_state;
int machine_event;
int pdu_op;

int processPdu(char *pdu) {
	int i, op;
	op = pdu[1];
	for (i = 0; i < 128 && pdu[i + 2] != '\0'; i++) {
		message[i] = pdu[i + 2];
	}
	return op;
}
/*	À espera de um pacote para enviar pela rede	*/
/*	Deve enviar pela rede o pacote 				*/
void PacketOverWP() {
	machine_state = WRR;
	machine_event = RR;
	bks_send_server_udp(out_pdu);
}

/*	Á espera que chegue um pacote da rede	*/
/*	Deve receber esse pacote				*/
void RROverWRR() {
	int op, tcp_sock = -1;
	char send_pdu[10];
	switch (get_data_from(get_udp_sock(), get_tcp_sock())) {
	case 1:
		bks_rcv_udp(in_pdu);
		break;
	case 2:
		tcp_sock = bks_accept();
		printf("recv -> %d\n", bks_rcv_tcp(tcp_sock, in_pdu));
		break;
	default:
		printf("ERROR:problemas no select");
	}
	printf("tcp_sock -> %d\n", tcp_sock);
	memset(message, 0, 128);
	op = processPdu(in_pdu);
	memset(in_pdu, 0, 128);
	printf("DEBUG de message: %s\n", message);
	switch (op) {
	case 1:
		receive_put(message);
		break;
	case 5:
		printf("Get\n");
		send_pdu[0] = 2;
		send_pdu[1] = 6;
		bks_send_tcp(tcp_sock, send_pdu);
		bks_disconnect(tcp_sock);
		break;
	case 7:
		printf("Get d + c\n");
		send_pdu[0] = 2;
		send_pdu[1] = 6;
		bks_send_tcp(tcp_sock, send_pdu);
		bks_disconnect(tcp_sock);
		break;
	case 8:
		printf("Get + c\n");
		send_pdu[0] = 2;
		send_pdu[1] = 6;
		bks_send_tcp(tcp_sock, send_pdu);
		bks_disconnect(tcp_sock);
		break;
	case 9:
		printf("Get + d\n");
		send_pdu[0] = 2;
		send_pdu[1] = 6;
		bks_send_tcp(tcp_sock, send_pdu);
		bks_disconnect(tcp_sock);
		break;
	case 11:
		receive_synch();
		processPdu(in_pdu);
		ID = strdup(message);
		printf("CASE 11\nSERVER ID: %s, STATE: %d, SON: %s\n", ID, STATE, SON);
		break;
	case 12:
		STATE = 2;
		printf("CASE 12\nSERVER ID: %s, STATE: %d, SON: %s\n", ID, STATE, SON);
		break;
	case 13:
		SON = strdup(message);
		printf("CASE 13\nSERVER ID: %s, STATE: %d, SON: %s\n", ID, STATE, SON);
		break;
	default:
		break;
	}
}

void PacketOverWRR() {
	printf("User Error");
	/*	AlertOnUserError;*/
}

void RROverWP() {
	printf("trnasmission Error");
	/*	if(N < > S)
	 AlertOnTransmissionError;*/
}
void TimerOverWP() {
	/*	AlertOnOperatingSystemError;*/
}

void TimerOverWRR() {
	/*	SendIFrame(1 - S);
	 FireTimer;*/
}

ActionType ActionsTable[3][2] = { { PacketOverWP, PacketOverWRR }, { RROverWP,
		RROverWRR }, { TimerOverWP, TimerOverWRR } };

/* MOBILIDADE DA BD */

char* get_locale_by_user(char* username) {
	int i;
	for (i = 0; USERS_LIST[i] != NULL; i++) {
		if (strcmp(USERS_LIST[i]->name, username) == 0)
			return USERS_LIST[i]->locale;
	}
	return NULL;
}

int get_server_by_locale(char* locale) {
	int i;
	for (i = 0; COUNTRIES_LIST[i]; i++) {
		if (strcmp(COUNTRIES_LIST[i]->locale, locale) == 0)
			return i;
	}
	return -1;
}

char* verify_migration(char* username) {
	char* locale;
	int idx, i;
	locale = strdup(get_locale_by_user(username));
	if (locale != NULL) {
		idx = get_server_by_locale(locale);
		free(locale);
	}
	if (strcmp(COUNTRIES_LIST[idx]->server, ID) == 0) {
		for (i = 0; COUNTRIES_LIST[i] != NULL; i++)
			servers_access[i] = 0;
		return NULL;
	}
	servers_access[idx] += 1;
	if (servers_access[idx] > 2) {
		return COUNTRIES_LIST[idx]->server;
	}
	return NULL;
}

/* FIM DA MOBILIDADE DA BD */

/* initiate the Users DB */
void read_users() {
	char* users = readFile(USERS_FILE);
	char *user, *name, *pass, *locale;
	char* user_aux_list[USERS_LIST_SIZE];
	int i = 0;
	user = strtok(users, "\n");
	while (user != NULL) {
		user_aux_list[i++] = user;
		user = strtok(NULL, "\n");
	}
	user_aux_list[i] = NULL;
	i = 0;
	while (user_aux_list[i] != NULL && USERS_LIST_SIZE > i) {
		name = strtok(user_aux_list[i], " \n\t");
		pass = strtok(NULL, " \n\t");
		locale = strtok(NULL, " \n\t");
		if (name == NULL || pass == NULL || locale == NULL)
			break;
		USERS_LIST[i] = create_user(name, pass, locale);
		++i;
	}

}

/* initiate the countries DB */
void read_countries() {
	char *countries = readFile(COUTRIES_FILE);
	char *locale, *name, *server, *country;
	char* country_aux_list[COUNTRIES_LIST_SIZE];
	int i = 0;
	country = strtok(countries, "\n");
	while (country != NULL) {
		country_aux_list[i++] = country;
		country = strtok(NULL, "\n");
	}
	country_aux_list[i] = NULL;
	i = 0;
	while (country_aux_list[i] != NULL && COUNTRIES_LIST_SIZE > i) {
		locale = strtok(country_aux_list[i], " \n\t");
		name = strtok(NULL, " \n\t");
		server = strtok(NULL, " \n\t");
		if (locale == NULL || name == NULL || server == NULL)
			break;
		COUNTRIES_LIST[i] = create_country(locale, name, server);
		++i;
	}
}

void insert_data(char *locale, char *number, char *date, int putsize) {
	char *put = (char*) malloc(sizeof(char) * putsize);
	memset(put, '\0', putsize);
	strcat(put, locale);
	strcat(put, " ");
	strcat(put, number);
	strcat(put, " ");
	strcat(put, date);
	put_list[put_count] = put;
	put_count++;
}

/* receive a put PDU */
void receive_put(char *pdu) {
	char *user, *pass, *locale, *number, *date;
	int putsize = 3;
	locale = strtok(pdu, " ");
	putsize += strlen(locale);
	number = strtok(NULL, " ");
	putsize += strlen(number);
	date = strtok(NULL, " ");
	putsize += strlen(date);
	user = strtok(NULL, " ");
	pass = strtok(NULL, "\0");
	if (check_user(USERS_LIST, 100, user, pass, locale) != 1) {
		return;
	}
	memset(out_pdu, '\0', 128);
	if (STATE != 2) {
		out_pdu[0] = strlen(SON) + 3;
		out_pdu[1] = 3;
		out_pdu = strcat(out_pdu, SON);
	} else {
		insert_data(locale, number, date, putsize);
		out_pdu[0] = 2;
		out_pdu[1] = 2;
	}
	machine_state = WP;
	machine_event = PACKET;
}

/* receive a get PDU */
int receive_get(char* user, char* pass) {
	printf("chegou Get - %s - %s\n", user, pass);
	return 0;
}

/* receive a synch PDU */
void receive_synch() {
	char label_str[8];
	char state_str[8];
	memset(out_pdu, 0, 128);
	sprintf(label_str, "%d", LABEL);
	sprintf(state_str, "%d", STATE);
	out_pdu[0] = 6 + strlen(SON) + strlen(label_str) + strlen(state_str)
			+ strlen(ID);
	out_pdu[1] = 14;
	strcat(out_pdu, label_str);
	strcat(out_pdu, " ");
	strcat(out_pdu, state_str);
	strcat(out_pdu, " ");
	strcat(out_pdu, SON);
	strcat(out_pdu, " ");
	strcat(out_pdu, ID);
	machine_state = WP;
	machine_event = PACKET;
}

/* main function */
int main(int argc, char *argv[]) {
	int port, i;
	ID = (char *) malloc(128 * sizeof(char));
	in_pdu = (char *) malloc(128 * sizeof(char));
	out_pdu = (char *) malloc(128 * sizeof(char));
	message = (char*) malloc(128 * sizeof(char));
	SON = "S0";
	STATE = 0;
	LABEL = 0;
	read_users();
	read_countries();
	for (i = 0; COUNTRIES_LIST[i] != NULL; i++)
		servers_access[i] = 0;

	if(argc == 2)
		port = atoi(argv[1]);
	switch (port) {
	case 8080:
		ID = "S1";
		break;
	case 8081:
		ID = "S2";
		break;
	case 8082:
		ID = "S3";
		break;
	case 8083:
		ID = "S4";
	}
	printf("id: %s\n", ID);
	bks_init_server_socket(port);
	machine_state = WRR;
	machine_event = RR;
	while (1) {
		ActionsTable[machine_event][machine_state]();
	}
	bks_close();
	return 0;
}
